/////////// VARIABLES
#include "SuperShaderVars.fx"

/////////// FUNCTIONS
#include "SuperShaderFuncs.fx"

//////// VERTEX SHADER
VertexShaderOutput VertexShaderSimple(VertexShaderInput input)
{
	VertexShaderOutput output = (VertexShaderOutput)0;

	float2 displacementTexCoord = input.TexCoord.xy * CurrentMaterial.DisplacementScale + CurrentMaterial.DisplacementOffset;
	
	float displacementSample = tex2Dlod(DisplacementTextureSampler, float4(displacementTexCoord.x, displacementTexCoord.y,0,0)).r;
	input.Position = input.Position + normalize(input.Normal)*displacementSample * CurrentMaterial.DisplacementAmount;	
	input.Position.w = 1;
	
	

	output.Position = mul(input.Position, WVP);
	output.PositionWorld = input.Position;
	output.PositionScreen = output.Position;
	
	output.Color = input.Color;
    output.TexCoord = input.TexCoord;
    output.NormalEye = mul(normalize(input.Normal), NormalMatrix);
    
    float4x4 world2 = World;
    world2._41 = 0;
    world2._42 = 0;
    world2._43 = 0;
   
	output.Normal = mul(input.Normal, world2);

    output.ShadowCoord = mul(input.Position, Lights[0].WVP);
    output.RealDistance = output.ShadowCoord.z;

	return output;
}

VertexShaderOutput VertexShaderEnd(VertexShaderInput input)
{
	VertexShaderOutput output = (VertexShaderOutput)0;

	float2 displacementTexCoord = input.TexCoord.xy * CurrentMaterial.DisplacementScale + CurrentMaterial.DisplacementOffset;
	
	//float displacementSample = tex2Dlod(DisplacementTextureSampler, float4(displacementTexCoord.x, displacementTexCoord.y,0,0)).r;
	//input.Position = input.Position + normalize(input.Normal)*displacementSample * CurrentMaterial.DisplacementAmount;	
	input.Position.y += 1.32*sin(input.Position.x/2 + Time)+sin(input.Position.x/5.3 + Time*0.3)*0.5+sin(input.Position.x/10 + Time*0.6)*2.1;
	//input.Position.z += 0.5*sin(input.Position.x + Time)+sin(input.Position.x*1.4f + Time*0.35)*0.3;
	

	
	//input.Position = mul(input.Position, twist);

	output.Position = mul(input.Position, WVP);
	output.PositionWorld = input.Position;
	output.PositionScreen = output.Position;
	
	output.Color = input.Color;
    output.TexCoord = input.TexCoord;
    output.NormalEye = mul(normalize(input.Normal), NormalMatrix);
    
    float4x4 world2 = World;
    world2._41 = 0;
    world2._42 = 0;
    world2._43 = 0;
   
	output.Normal = mul(input.Normal, world2);

    output.ShadowCoord = mul(input.Position, Lights[0].WVP);
    output.RealDistance = output.ShadowCoord.z;

	return output;
}



VertexShaderOutput VertexShaderTunnel(VertexShaderInput input)
{
	VertexShaderOutput output = (VertexShaderOutput)0;
	
	float4 pos = input.Position;
	pos.xy *= sin(input.TexCoord.x*3.14*6+2*sin(input.TexCoord.x*3.14*8+Time*3+GeggStep*10))*(1.0-GeggStep)*0.7+3;
	pos.x += sin(GeggStep*3.14/2)*100.0;
	output.Color = CurrentMaterial.Diffuse;
	
	output.ShadowCoord = mul(pos, Lights[0].WVP);
	output.Position = mul(pos, WVP);
	
    output.TexCoord = input.TexCoord;
    output.NormalEye = mul(normalize(input.Normal), NormalMatrix);
    
    float4x4 world2 = World;
    world2._41 = 0;
    world2._42 = 0;
    world2._43 = 0;
        
	output.Normal = mul(input.Normal, world2);
			
	output.PositionWorld = input.Position;
	output.PositionScreen = output.Position;
	
    output.RealDistance = output.ShadowCoord.z;
	
	return output;
}

VertexShaderOutput VertexShaderShadow(VertexShaderInput input)
{
	VertexShaderOutput output = VertexShaderSimple(input);

	// Recalc some shadow stuffs
	output.Position = mul(output.PositionWorld, Lights[0].WVP);
	output.PositionWorld = output.PositionWorld;
	output.PositionScreen = output.PositionWorld;
    output.ShadowCoord = mul(output.PositionWorld, Lights[0].WVP);
    output.RealDistance = output.ShadowCoord.z;

	return output;
}

VertexShaderOutput VertexShaderTunnelShadow(VertexShaderInput input)
{
	VertexShaderOutput output = (VertexShaderOutput)0;
	
	float4 pos = input.Position;
	pos.xy *= sin(input.TexCoord.x*3.14*6+2*sin(input.TexCoord.x*3.14*8+Time*3+GeggStep*10))*(1.0-GeggStep)*0.7+3;
	pos.x += sin(GeggStep*3.14/2)*100.0;
	output.Color = CurrentMaterial.Diffuse;
	output.Position = mul(pos, Lights[0].WVP);
	
    output.TexCoord = input.TexCoord;
    output.NormalEye = mul(normalize(input.Normal), NormalMatrix);
    
    float4x4 world2 = World;
    world2._41 = 0;
    world2._42 = 0;
    world2._43 = 0;
        
	output.Normal = mul(input.Normal, world2);
			
	output.PositionWorld = input.Position;
	output.PositionScreen = output.Position;

	// Recalc some shadow stuffs
    output.ShadowCoord = mul(pos, Lights[0].WVP);
    output.RealDistance = output.ShadowCoord.z;

	return output;
}





VertexShaderOutput VertexShader2D(VertexShaderInput input)
{
	VertexShaderOutput output = (VertexShaderOutput)0;
	
	output.Position = input.Position;
	output.PositionWorld = input.Position;
	output.PositionScreen = input.Position;
	output.TexCoord = input.TexCoord;

	return output;
}


//////// PIXEL SHADER
PixelShaderOutput PixelShaderSimple(VertexShaderOutput input)
{
	PixelShaderOutput output;
	output.Color = 0;
		
	float4 diffuseColor = 1;
	if (CurrentMaterial.UseDiffuseTexture)
		diffuseColor = tex2D(DiffuseTextureSampler, input.TexCoord);	
		
	for (int i = 0; i < 3; i++)
	{
		Light light = Lights[i];
		float3 lightW = mul(light.Position, View);
		float3 color = diffuselight(input.Normal, lightW, 0.5)*light.Color;
		float3 specular = blinnspecular(input.Normal, light.Position, CameraPosition, CurrentMaterial.Gloss)*CurrentMaterial.Specular;
		output.Color.rgb += CurrentMaterial.Diffuse*(color+specular);
	}
	
	output.Color.rgb = output.Color * diffuseColor + Ambient;
	output.Color.a = 1;
	return output;
}

PixelShaderOutput PixelShaderSimpleWShadow(VertexShaderOutput input)
{
	PixelShaderOutput output = PixelShaderSimple(input);

    float2 ProjectedTexCoords;
	ProjectedTexCoords[0] = input.ShadowCoord.x/input.ShadowCoord.z/2.0f +0.5f;
    ProjectedTexCoords[1] = -input.ShadowCoord.y/input.ShadowCoord.z/2.0f +0.5f;
    
    //float StoredDepthInShadowMap = tex2D(ShadowMapSampler, ProjectedTexCoords).x;
	float StoredDepthInShadowMap = PCSS(float4(ProjectedTexCoords, input.RealDistance.x - Bias,0)).x;
	output.Color *= saturate(StoredDepthInShadowMap)/2+0.5 + CurrentMaterial.SelfIllumination;
	return output;
}

PixelShaderOutput PixelShaderShadow(VertexShaderOutput input)
{
	PixelShaderOutput output;
	output.Color = input.ShadowCoord.z;///MaxDepth;
	return output;
}

PixelShaderOutput PixelShaderSSAONormals(VertexShaderOutput input)
{
	PixelShaderOutput output;
	float z = input.PositionScreen.z/MaxDepth;
	float3 n = normalize(float3(input.NormalEye.x,input.NormalEye.y,input.NormalEye.z));
	output.Color = float4((n.x+1)/2, 1-((n.y+1)/2), 1-((n.z+1)/2), z);
	output.Color = float4(n.x, n.y, n.z, z);
	return output;
}

//////////// SSAO Stuff
const int passes = 16;
uniform float totStrength = 1.38;
uniform float strength = 0.07;
uniform float offset = 18.0;
uniform float falloff = 0.00002;
uniform float rad = 0.006;
const float invSamples = 1.0/32.0;

static const float3 pSphere[] = {float3(0.53812504, 0.18565957, -0.43192),float3(0.13790712, 0.24864247, 0.44301823),float3(0.33715037, 0.56794053, -0.005789503),float3(-0.6999805, -0.04511441, -0.0019965635),float3(0.06896307, -0.15983082, -0.85477847),float3(0.056099437, 0.006954967, -0.1843352),float3(-0.014653638, 0.14027752, 0.0762037),float3(0.010019933, -0.1924225, -0.034443386),float3(-0.35775623, -0.5301969, -0.43581226),float3(-0.3169221, 0.106360726, 0.015860917),float3(0.010350345, -0.58698344, 0.0046293875),float3(-0.08972908, -0.49408212, 0.3287904),float3(0.7119986, -0.0154690035, -0.09183723),float3(-0.053382345, 0.059675813, -0.5411899),float3(0.035267662, -0.063188605, 0.54602677),float3(-0.47761092, 0.2847911, -0.0271716)};
/*
static const float3 pSphere[] = {
	float3(-0.010735935, 0.01647018, 0.0062425877),float3(-0.06533369, 0.3647007, -0.13746321),
	float3(-0.6539235, -0.016726388, -0.53000957),float3(0.40958285, 0.0052428036, -0.5591124),
	float3(-0.1465366, 0.09899267, 0.15571679),float3(-0.44122112, -0.5458797, 0.04912532),
	float3(0.03755566, -0.10961345, -0.33040273),float3(0.019100213, 0.29652783, 0.066237666),
	float3(0.8765323, 0.011236004, 0.28265962),float3(0.29264435, -0.40794238, 0.15964167)
	};
*/
float4 SSAOCombinePixelShader(VertexShaderOutput input) : COLOR0
{
   // grab a normal for reflecting the sample rays later on
   float3 fres = normalize((tex2D(TextureSampler,input.TexCoord*offset).xyz*2.0) - float3(1,1,1));
   float4 currentPixelSample = tex2D(SSAOTextureSampler, input.TexCoord);
   float currentPixelDepth = currentPixelSample.a;
 
   // current fragment coords in screen space
   float3 ep = float3(input.TexCoord.xy, currentPixelDepth);
   
   // get the normal of current fragment
   float3 norm = currentPixelSample.xyz;
   //norm = (norm-0.5)*2;
   //norm.y = 1.0 - norm.y;
 
   float bl = 0.0;
   // adjust for the depth ( not shure if this is good..)
   float radD = rad/currentPixelDepth;
 
   //float3 ray, se, occNorm;
   float occluderDepth, depthDifference;
   float4 occluderFragment;
   float3 ray;
   for(int i=0; i < passes; ++i)
   {
      // get a vector (randomized inside of a sphere with radius 1.0) from a texture and reflect it
      ray = radD*reflect(pSphere[i],fres);
 
      // get the depth of the occluder fragment
      occluderFragment = tex2D(SSAOTextureSampler, ep.xy + sign(dot(ray,norm))*ray.xy);
      occluderFragment.xyz = occluderFragment.xyz;
   
      // if depthDifference is negative = occluder is behind current fragment
      depthDifference = currentPixelDepth - occluderFragment.a;
 
      // calculate the difference between the normals as a weight
	  // the falloff equation, starts at falloff and is kind of 1/x^2 falling
      bl += 2*step(falloff,depthDifference)*
			(1.0-dot(occluderFragment.xyz, norm))*
			(1.0-smoothstep(falloff,strength,depthDifference));
   }
 
   // output the result
   
   //float c = bl/passes;//*invSamples
   float c = 1.0-(totStrength*bl*1.5*invSamples);
   
	//float c = currentPixelDepth;
   return float4(c,c,c,1);
}

//////// TECHNIQUES
technique SimpleTechnique
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderSimple();
        PixelShader = compile ps_3_0 PixelShaderSimple();
    }
}

technique TunnelTechnique
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderTunnel();
        PixelShader = compile ps_3_0 PixelShaderSimple();
    }
}

technique TunnelWShadowTechnique
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderTunnel();
        PixelShader = compile ps_3_0 PixelShaderSimpleWShadow();
    }
}

technique SimpleWShadowTechnique
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderSimple();
        PixelShader = compile ps_3_0 PixelShaderSimpleWShadow();
    }
}

technique ShadowTechnique
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderShadow();
        PixelShader = compile ps_3_0 PixelShaderShadow();
    }
}

technique TunnelShadowTechnique
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderTunnelShadow();
        PixelShader = compile ps_3_0 PixelShaderShadow();
    }
}

technique SSAONormalTechnique
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderSimple();
        PixelShader = compile ps_3_0 PixelShaderSSAONormals();
    }
}

technique SSAOCombineTechnique
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShader2D();
        PixelShader = compile ps_3_0 SSAOCombinePixelShader();
    }
}


technique EndTechnique
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderEnd();
        PixelShader = compile ps_3_0 PixelShaderSimple();
    }
}

technique EndWShadowTechnique
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderEnd();
        PixelShader = compile ps_3_0 PixelShaderSimple();
    }
}